//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <kcrt.h>
#include <mantle.h> // for kprintf
#include <clsinfo.h>
#include <kmarshal.h>
#include <stub.h>
#include <syscall.h>
#include <eladef.h>

static CIClassInfoNode *s_pSharedClassInfoList = NULL;

#ifdef DEBUG_KMUTEX
static KMutex s_sharedClsInfoLock(__FILE__, __LINE__);
#else
static KMutex s_sharedClsInfoLock;
#endif //DEBUG_KMUTEX

UInt32 g_uShareMemStart = 0;
// TODO: need mutex to protect it
static UInt32 s_uShareMemBroke = 0;

// TODO: maybe need lock for share Memory
//
inline int AllocFromPool(int size)
{
    int oldPos = s_uShareMemBroke;
    s_uShareMemBroke += ROUNDUP2(size, sizeof(Int32));
    if (s_uShareMemBroke >= g_uShareMemStart + MAX_SHARED_MEM_OF_CLSINFO) {
        return 0;
    }
    return oldPos;
}

ECode RegisterSharedClassInfo(
    /* [in] */ CIClassInfo *pClassInfo)
{
    CIClassInfo * pShareClassInfo;
    CIClassInfoNode *pNewNode;
    CIClassInfoNode *pCurNode;
    CIClassEntry    *pClassDir;
    CIInterfaceEntry *pInterfaceDir;

    if (!pClassInfo) return E_INVALID_ARGUMENT;

    if (s_uShareMemBroke == 0) {
        s_uShareMemBroke = g_uShareMemStart;
    }

    // TODO: compare component uuid, need car support
    // prevent from registering same ClassInfo again
    //
    s_sharedClsInfoLock.Lock();
    if (pClassInfo->classNum != 0) {
        pClassDir = (CIClassEntry *)((UInt32)pClassInfo->classDir \
                            + (UInt32)pClassInfo);
        pCurNode = s_pSharedClassInfoList;
        while (pCurNode != NULL) {
            if (pCurNode->m_pClsInfo->classNum != 0 \
                && pClassDir[0].clsid == \
                    pCurNode->m_pClsInfo->classDir[0].clsid) {
                break;
            }
            pCurNode = pCurNode->m_pNext;
        }
    }
    else if (pClassInfo->interfaceNum != 0) {
        pInterfaceDir = (CIInterfaceEntry *)((UInt32)pClassInfo->interfaceDir \
                                + (UInt32)pClassInfo);
        pCurNode = s_pSharedClassInfoList;
        while (pCurNode != NULL) {
            if (pCurNode->m_pClsInfo->interfaceNum != 0 \
                && pInterfaceDir[0].iid == \
                    pCurNode->m_pClsInfo->interfaceDir[0].iid) {
                break;
            }
            pCurNode = pCurNode->m_pNext;
        }
    }
    else {
        s_sharedClsInfoLock.Unlock();
        // No class info need to register
        return NOERROR;
    }

    if (pCurNode != NULL) {
        s_sharedClsInfoLock.Unlock();
        return S_ALREADY_EXISTS;
    }

    pShareClassInfo = (CIClassInfo *)AllocFromPool(pClassInfo->totalSize);
    s_sharedClsInfoLock.Unlock();
    if (NULL == pShareClassInfo) {
        return E_OUT_OF_MEMORY;
    }
    RelocateClassInfo(pClassInfo, pShareClassInfo);

    pNewNode = (CIClassInfoNode *)malloc(sizeof(CIClassInfoNode));
    if (pNewNode == NULL) {
        return E_OUT_OF_MEMORY;
    }
    pNewNode->m_pClsInfo = (CIClassInfo *)pShareClassInfo;
    pNewNode->m_pNext = NULL;
    s_sharedClsInfoLock.Lock();
    if (s_pSharedClassInfoList == NULL) {
        s_pSharedClassInfoList = pNewNode;
    }
    else {
        pNewNode->m_pNext = s_pSharedClassInfoList->m_pNext;
        s_pSharedClassInfoList->m_pNext = pNewNode;
    }
    s_sharedClsInfoLock.Unlock();

    return NOERROR;
}

ECode UnregisterSharedClassInfo(
    /* [in] */ CIClassInfo *pClassInfo)
{
    CIClassInfoNode *pCurNode, *pPreNode;

    assert(pClassInfo != NULL);

    s_sharedClsInfoLock.Lock();
    pPreNode = NULL;
    pCurNode = s_pSharedClassInfoList;
    while (pCurNode != NULL) {
        if ((*pCurNode).m_pClsInfo == pClassInfo) {
            break;
        }
        pPreNode = pCurNode;
        pCurNode = pCurNode->m_pNext;
    }

    if (pCurNode != NULL) {
        if (pPreNode == NULL) {
            s_pSharedClassInfoList = pCurNode->m_pNext;
        }
        else {
            pPreNode->m_pNext = pCurNode->m_pNext;
        }
        s_sharedClsInfoLock.Unlock();
        free(pCurNode);

        return NOERROR;
    }

    s_sharedClsInfoLock.Unlock();
    return E_NO_CLASS_INFO;
}

ECode LookupSharedClassEntry(
    REMuid rclsid,
    CIClassEntry **ppClassEntry)
{
    CIClassInfoNode *pCurNode;
    CIClassInfo *pClsInfo;
    int m;

    assert(ppClassEntry != NULL);

    s_sharedClsInfoLock.Lock();
    pCurNode = s_pSharedClassInfoList;
    while (pCurNode != NULL) {
        pClsInfo = pCurNode->m_pClsInfo;
        for (m = 0; m < pClsInfo->classNum; m++) {
            if (pClsInfo->classDir[m].clsid == rclsid) {
                *ppClassEntry = &(pClsInfo->classDir[m]);
                s_sharedClsInfoLock.Unlock();
                return NOERROR;
            }
        }
        pCurNode = pCurNode->m_pNext;
    }
    s_sharedClsInfoLock.Unlock();
    MARSHAL_DBGOUT(MSHDBG_NORMAL, DUMP_GUID(rclsid));

    return E_ENTRY_NOT_FOUND;
}
